#include "global.h"

#define SCL_H (I2C_PORT->BSRR = I2C_PIN_SCL, i2c_delay(5))
#define SCL_L (I2C_PORT->BRR = I2C_PIN_SCL, i2c_delay(5))
#define SDA_H (I2C_PORT->BSRR = I2C_PIN_SDA, i2c_delay(5))
#define SDA_L (I2C_PORT->BRR = I2C_PIN_SDA, i2c_delay(5))
#define SDA_READ (i2c_delay(5), (I2C_PORT->IDR & I2C_PIN_SDA) ? 1 : 0)

// #define SCL_H (I2C_PORT->BSRR = I2C_PIN_SCL, __nop(), __nop())
// #define SCL_L (I2C_PORT->BRR = I2C_PIN_SCL, __nop(), __nop())
// #define SDA_H (I2C_PORT->BSRR = I2C_PIN_SDA, __nop(), __nop())
// #define SDA_L (I2C_PORT->BRR = I2C_PIN_SDA, __nop(), __nop())
// #define SDA_READ ((I2C_PORT->IDR & I2C_PIN_SDA) ? 1 : 0)

/**
 * 延迟函数。
 *
 * 功能描述：
 * - 使用简单的循环实现延迟，参数指定延迟的时钟周期数。
 *
 * 参数：
 * - i：延迟的时钟周期数。
 */
void i2c_delay(uint32_t i)
{
    while (i--)
        ;
}

/**
 * 初始化软件 I2C 接口。
 *
 * 功能描述：
 * 1. 使能 I2C 模块的时钟。
 * 2. 配置 SCL 和 SDA 引脚为开漏输出模式，速度为 50MHz。
 * 3. 初始化 SCL 和 SDA 引脚为高电平（释放总线）。
 */
void i2c_init(void)
{
    RCC_APB2PeriphClockCmd(I2C_RCC, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
    GPIO_InitStructure.GPIO_Pin = I2C_PIN_SCL | I2C_PIN_SDA;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_PORT, &GPIO_InitStructure);

    // 初始化引脚为高电平（释放总线）
    SCL_H;
    SDA_H;
}

/**
 * 发送 I2C 起始信号。
 *
 * 功能描述：
 * 1. 确保 SDA 和 SCL 为高电平。
 * 2. 在 SCL 高电平期间，拉低 SDA，产生起始信号。
 * 3. 拉低 SCL，占用总线并准备后续通信。
 */
void i2c_start(void)
{
    SDA_H; // 确保 SDA 为高电平
    SCL_H; // 确保 SCL 为高电平
    SDA_L; // 在 SCL 高电平期间，拉低 SDA，产生起始信号
    SCL_L; // 拉低 SCL，占用总线
}

/**
 * 发送 I2C 终止信号。
 *
 * 功能描述：
 * 1. 拉低 SDA，确保 SDA 为低电平。
 * 2. 释放 SCL，使其呈现高电平。
 * 3. 在 SCL 高电平期间，释放 SDA，产生终止信号。
 */
void i2c_stop(void)
{
    SDA_L; // 拉低 SDA
    SCL_H; // 释放 SCL，使其呈现高电平
    SDA_H; // 在 SCL 高电平期间，释放 SDA，产生终止信号
}

/**
 * 发送一个字节数据。
 *
 * 功能描述：
 * 1. 循环 8 次，依次发送字节的每一位数据。
 * 2. 在 SCL 高电平期间，从机读取 SDA 的数据。
 *
 * 参数：
 * - byte：需要发送的字节数据。
 */
void i2c_sendByte(uint8_t byte)
{
    uint8_t i;

    for (i = 0; i < 8; i++) // 循环 8 次，发送每一位数据
    {
        if (byte & (0x80 >> i)) // 根据当前位的值设置 SDA 状态
        {
            SDA_H;
        }
        else
        {
            SDA_L;
        }
        SCL_H; // 释放 SCL，从机在 SCL 高电平期间读取 SDA
        SCL_L; // 拉低 SCL，主机开始发送下一位数据
    }
}

/**
 * 接收一个字节数据。
 *
 * 功能描述：
 * 1. 循环 8 次，依次接收字节的每一位数据。
 * 2. 在 SCL 高电平期间，主机读取 SDA 的数据。
 *
 * 返回值：
 * - 接收到的一个字节数据。
 */
uint8_t i2c_receiveByte(void)
{
    uint8_t i, byte = 0x00; // 定义接收的数据，并赋初值 0x00

    SDA_H;                  // 接收前，主机先确保释放 SDA
    for (i = 0; i < 8; i++) // 循环 8 次，接收每一位数据
    {
        SCL_H; // 释放 SCL，主机在 SCL 高电平期间读取 SDA
        if (SDA_READ == 1)
        {
            byte |= (0x80 >> i); // 当 SDA 为 1 时，置变量指定位为 1
        }
        SCL_L; // 拉低 SCL，从机在 SCL 低电平期间写入 SDA
    }
    return byte; // 返回接收到的一个字节数据
}

/**
 * 发送应答位。
 *
 * 功能描述：
 * 1. 根据参数值设置 SDA 的状态（应答或非应答）。
 * 2. 在 SCL 高电平期间，从机读取 SDA 的应答位。
 *
 * 参数：
 * - ackBit：应答位值（1 表示应答，0 表示非应答）。
 */
void i2c_sendAck(uint8_t ackBit)
{
    if (ackBit) // 设置应答位
    {
        SDA_L;
    }
    else
    {
        SDA_H;
    }
    SCL_H; // 释放 SCL，从机在 SCL 高电平期间读取应答位
    SCL_L; // 拉低 SCL，开始下一个时序模块
}

/**
 * 接收应答位。
 *
 * 功能描述：
 * 1. 在 SCL 高电平期间，主机读取 SDA 的应答位。
 *
 * 返回值：
 * - 应答位值（1 表示应答，0 表示非应答）。
 */
uint8_t i2c_receiveAck(void)
{
    uint8_t ackBit; // 定义应答位变量

    SDA_H;             // 接收前，主机先确保释放 SDA
    SCL_H;             // 释放 SCL，主机在 SCL 高电平期间读取 SDA
    ackBit = SDA_READ; // 将应答位存储到变量里
    SCL_L;             // 拉低 SCL，开始下一个时序模块
    return ackBit;     // 返回应答位值
}

/**
 * 忽略应答位。
 *
 * 功能描述：
 * 1. 释放 SCL 并拉低 SCL，跳过应答位的处理。
 */
void i2c_ignoreAck(void)
{
    SCL_H; // 释放 SCL
    SCL_L; // 拉低 SCL，开始下一个时序模块
}
